package com.hongyun.tms.infra.excel.listener;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.alibaba.excel.metadata.data.CellData;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.hongyun.tms.common.enums.BizCode;
import com.hongyun.tms.exception.BusinessRuntimeException;
import com.hongyun.tms.exception.DictNotFoundException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Component;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 导入监听器父类
 *
 * @author liPeng
 * @date 2024-09-10
 */
@Slf4j
@Component
public class DefaultExcelListener<T> extends AnalysisEventListener<T> {

    private final List<T> rows = Lists.newArrayList();
    private final static Integer SPLIT_THRESHOLD = 500;
    private final Validator validator;

    public DefaultExcelListener() {
        // 使用 javax.validation.Validator 进行校验
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        this.validator = factory.getValidator();
    }


    /**
     * 读取excel数据前操作(只有不读取表头数据时才会触发此方法)
     */
    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        log.info("======================================================");
        log.info("解析第一行数据:{}" + JSON.toJSONString(headMap));
        log.info("======================================================");
    }

    /**
     * 读取excel数据操作
     *
     * @param object
     * @param context
     */
    @Override
    public void invoke(T object, AnalysisContext context) {

        // 触发校验
        Set<ConstraintViolation<T>> violations = validator.validate(object);
        if (CollectionUtils.isNotEmpty(violations)) {
            // 构建详细的错误信息
            StringBuilder errorMessage = new StringBuilder();
            for (ConstraintViolation<T> violation : violations) {
                // 记录错误信息
                String message = String.format("第%d行数据：%s", context.readRowHolder().getRowIndex() + 1, violation.getMessage());
                log.error(message);
                errorMessage.append(message).append("; ");
            }
            // 抛出校验异常，并附带详细错误信息
            throw new BusinessRuntimeException(BizCode.EXCEL_ERROR, errorMessage.toString());
        } else {
            rows.add(object);
        }
//        log.info("list容量" + rows.size() + "---" + object);
    }

    /**
     * 读取完excel数据后的操作
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        log.info("成功读取【" + rows.size() + "】条数据");
    }

    /**
     * 在读取excel异常 获取其他异常下会调用本接口。抛出异常则停止读取。如果这里不抛出异常则 继续读取下一行。
     */
    @Override
    public void onException(Exception exception, AnalysisContext context) {
        log.error("解析失败，但是继续解析下一行:{}", exception.getMessage());

        if (exception instanceof ExcelDataConvertException) {
            ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException) exception;
            log.error("第{}行，第{}列解析异常，数据为:{}",
                    excelDataConvertException.getRowIndex() + 1,
                    excelDataConvertException.getColumnIndex() + 1,
                    getCellDataValue(excelDataConvertException.getCellData()));

            // 抛出自定义异常，提供更多信息
            throw new BusinessRuntimeException(BizCode.EXCEL_ERROR,
                    String.format("第%s行，第%s列解析异常，数据为:%s",
                            excelDataConvertException.getRowIndex() + 1,
                            excelDataConvertException.getColumnIndex() + 1,
                            getCellDataValue(excelDataConvertException.getCellData())));
        } else if (exception instanceof DictNotFoundException) {
            // 捕获自定义的字典值异常，输出行号和错误信息
            log.error("第{}行，字典转换异常，原因:{}", context.readRowHolder().getRowIndex() + 1, exception.getMessage());
            throw new BusinessRuntimeException(BizCode.EXCEL_ERROR,
                    "第" + (context.readRowHolder().getRowIndex() + 1) + "行字典值错误：" + exception.getMessage());
        }
    }

    private String getCellDataValue(CellData<?> cellData) {
        if (cellData == null) {
            return "null";
        }

        // 根据数据类型返回值
        if (cellData.getType() == CellDataTypeEnum.NUMBER) {
            return cellData.getNumberValue().toString();
        } else if (cellData.getType() == CellDataTypeEnum.BOOLEAN) {
            return cellData.getBooleanValue().toString();
        } else {
            // 默认返回字符串类型的数据
            return cellData.getStringValue();
        }
    }


    /**
     * @return 返回读取excel总数据
     */
    public List<T> getRows() {
        return rows;
    }

    public Integer getSplitThreshold() {
        return SPLIT_THRESHOLD;
    }
}
